// © Головин Г.Г., Визуализация игрового процесса, 2023
'use strict';
// размер клеточки, отступ
const size = 30, gap = 2;
// массив статусов игры
const statuses = ["ИГРА","УРОВЕНЬ","ПАУЗА","ЗАВЕРШЕНО"];
// массив цветов для фигур
const colors = [
  'rgba(240,240,240,1)',
  'rgba(150,0,0,1)',
  'rgba(0,0,150,1)',
  'rgba(150,150,0,1)',
  'rgba(0,150,150,1)',
  'rgba(0,150,0,1)',
  'rgba(150,0,150,1)',
  'rgba(150,80,80,1)'];
// прозрачность фигур
colors.setDefault = function() {
  this.alpha = 0;
}
colors.setDefault();
// получаем цвет, добавляем прозрачность только фигурам
colors.get = function(...numbers) {
  for (let num of numbers)
    if (num <= 0) return this[0];
    else if (num < colors.length)
      return this[num].replace('1)', (100-this.alpha)/100 + ')');
  return this[0];
}
// массив для кубиков
let field3D = [];
// центральная точка для поворотов
const t0 = {}; t0.reCalc = function() {
  this.x = columns*(size+gap)/2;
  this.y = rows*(size+gap);
  this.z = (size+gap)*2;
};
t0.reCalc();
// угол поворота игрового поля с кубиками
let deg = {}; deg.setDefault = function() {
  this.x=-1;
  this.y=0;
  this.z=0;
};
deg.setDefault();
// параллельная проекция: центр и экран наблюдателя
let d1, tv1 = {}; tv1.reCalc = function() {
  this.x=columns*(size+gap)/2;
  this.y=(size+gap)*2;
  this.z=(size+gap)*2;
  d1 = Math.max(rows,columns)*(size+gap);
}
tv1.reCalc();
// перспективная проекция: центр и экран наблюдателя
let d2, d2max, tv2 = {}; tv2.reCalc = function() {
  this.x = columns*(size+gap)/2;
  this.y = rows*(size+gap)/2;
  this.z = (size+gap)*2;
  this.show=false;
  d2 = Math.max(rows,columns)*(size+gap); d2max = d2*2;
};
tv2.reCalc();
// стакан с игрой
const container = {};
container.canvas = document.getElementById('container');
container.context = container.canvas.getContext('2d');
container.changeSize = function() {
  this.canvas.width = columns*size+(columns+1)*gap;
  this.canvas.height = rows*size+(rows+1)*gap;
};
container.changeSize();
// перетаскивание центральной точки мышью
container.msBtnPressed = false;
container.canvas.onmouseup = ()=> container.msBtnPressed=false;
container.canvas.onmousedown = (caller)=> {
  container.msBtnPressed=true;
  container.canvas.onmousemove(caller);
}
container.canvas.onmousemove = (caller)=> {
  if (vol==VOLUME.PERSPECTIVE && container.msBtnPressed && tv2.show) {
    tv2.x = caller.offsetX;
    tv2.y = caller.offsetY;
    repaint(false);
    refreshParams();
  }
}
// следующая фигура
const next = {};
next.canvas = document.getElementById('next');
next.context = next.canvas.getContext('2d');
next.changeSize = function() {
  this.canvas.width = 2*size+3*gap;
  this.canvas.height = 4*size+5*gap;
};
next.changeSize();
// состояние игры
const statusView = {};
statusView.level = document.getElementById('levelView');
statusView.score = document.getElementById('scoreView');
statusView.nextLevel = document.getElementById('nextLevelView');
statusView.speed = document.getElementById('speedometer')
statusView.status = document.getElementById('statusView');
statusView.refresh = function() {
  this.level.innerText = level;
  this.score.innerText = score;
  this.nextLevel.innerText = nextLevel;
  this.speed.value = speedometer();
  this.status.innerText = statuses[status];
}
// обновление изображения
function repaint(refresh3D=true) {
  // состояние игры
  statusView.refresh();
  // стакан с игрой
  if (vol==VOLUME.FLAT) {
    drawCells(container.canvas, container.context, field, currentFigure.type);
  } else {
    if (refresh3D || field3D.length==0)
      prepare3D();
    repaint3D();
  }
  // следующая фигура
  drawCells(next.canvas, next.context, nextFigure.shape);
}
// рисуем массив клеточек
function drawCells(canvas, context, array, color) {
  // очищаем весь холст целиком
  context.clearRect(0, 0, canvas.width, canvas.height);
  // обходим массив, рисуем клеточки
  for (let y=0; y<array.length; y++)
    for (let x=0; x<array[y].length; x++) {
      context.fillStyle = colors.get(array[y][x], color);
      context.fillRect(gap+x*(size+gap), gap+y*(size+gap), size, size);
    }
}
// обходим поле, создаём кубики
function prepare3D() {
  field3D = [];
  for (let y=0; y<field.length; y++)
    for (let x=0; x<field[y].length; x++) {
      let cube = new Cube(gap+x*(size+gap),gap+y*(size+gap),size+gap,size+gap);
      // в пустых кубиках оставляем только заднюю стенку
      if (field[y][x] == 0) {
        for (let i=0;i<3;i++) cube.faces.shift();
        for (let i=0;i<2;i++) cube.faces.pop();
      }
      cube.rotate(deg, t0);
      cube.color = field[y][x] < 11 ? field[y][x] : currentFigure.type;
      field3D.push(cube);
    }
}
// рисуем массив кубиков
function repaint3D() {
  // проекции граней кубиков
  let proj = [];
  // получаем массив проекций
  for (let cube of field3D) {
    let cProj;
    if (vol==VOLUME.PARALLEL)
      cProj = cube.projection('parallel',tv1,d1);
    else
      cProj = cube.projection('perspective',tv2,d2);
    for (let face of cProj)
      face.color = cube.color;
    proj = proj.concat(cProj);
  }
  // сортируем грани по удалённости от центра проекции
  proj.sort((a,b) => b.dist-a.dist);
  // удаляем смежные стенки между соседними кубиками
  for (let i=0, j=1; i<proj.length-1; j=++i+1)
    while (j<proj.length && Cube.pEquidistant(proj[i],proj[j]))
      if (Cube.pAdjacent(proj[i],proj[j])) {
        proj.splice(j,1);
        proj.splice(i,1);
        i--; j=proj.length;
      } else j++;
  if (vol==VOLUME.PARALLEL)
    // сортируем грани разных кубиков по удалённости и внутри одного кубика по наклону
    proj.sort((a,b)=>Math.abs(b.dist-a.dist)>size ? b.dist-a.dist : b.clock-a.clock);
  // обновляем стакан с игрой
  drawCubes(container.canvas, container.context, proj);
  // центральная точка перспективной проекции
  if (vol==VOLUME.PERSPECTIVE && tv2.show) centerPoint(container.context);
}
// обходим массив, рисуем грани по точкам
function drawCubes(canvas, context, array) {
  // очищаем весь холст целиком
  context.clearRect(0, 0, canvas.width, canvas.height);
  // рисуем только видимые грани
  const visible = function(face) {
    // если есть хотя бы одна точка, которую можно нарисовать
    for (let j = 0; j < face.length; j++)
      if (face[j].x>-size && face[j].x<canvas.width+size
       && face[j].y>-size && face[j].y<canvas.height+size)
        return true;
    return false;
  };
  // обходим массив граней куба
  for (let i = 0; i < array.length; i++) {
    // рисуем только видимые грани
    if (!visible(array[i])) continue;
    // обходим массив точек и соединяем их линиями
    context.beginPath();
    for (let j = 0; j < array[i].length; j++)
      if (j==0) context.moveTo(array[i][j].x, array[i][j].y);
      else context.lineTo(array[i][j].x, array[i][j].y);
    context.closePath();
    // рисуем линии на холсте
    context.lineWidth = 1.7;
    context.lineJoin = 'round';
    context.fillStyle = colors.get(array[i].color);
    context.strokeStyle = '#ffff';
    context.fill();
    context.stroke();
  }
}
// центральная точка перспективной проекции
function centerPoint(context) {
  context.beginPath();
  context.lineWidth = 3.2;
  context.strokeStyle = '#66bb6a';
  context.arc(tv2.x, tv2.y, 6.5, 0, 2*Math.PI);
  context.stroke();
}
// скорость падения фигуры
const speedometer = function() {
  let speed = 0;
  return function() {
    if (rapidFall)
      speed = Math.min(START_DELAY, speed + 80);
    else
      speed = Math.max(0, speed - 80);
    return Math.max(speed, START_DELAY - stepDelay);
  }
}();
